package mygame.hudelements;

import com.jme3.asset.AssetManager;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import com.jme3.ui.Picture;

/**
 * Abstract class for all instruments. The class encapsulates the basic
 * functionality to place any instrument on the HUD
 *
 * @author Aicher
 * @version 0.0.1
 */
public abstract class InstrumentControl extends AbstractControl {

    /**
     * Reference to the gui node
     */
    protected Node guiNode = null;
    /**
     * All elements will be tied to the centralNode - so it is easier to hide
     * the elements
     */
    protected Node centralInstrumentNode = null;
    /**
     * Reference to the asset manager
     */
    protected AssetManager assetManager = null;
    /**
     * This is the central value for calculating the size values. All measures
     * are derived from this value
     */
    protected float hudReferenceSize = 0;
    /**
     * Left side of the instrument
     */
    protected float instrumentLeftGridPosX = 0f;
    /**
     * Lower side of the instrument
     */
    protected float instrumentLowerGridPosY = 0f;
    /**
     * Size of the instrument (width and height) in Grid values
     */
    protected float instrumentGridWidth = 0;
    /**
     * Size of the instrument (width and height) in Grid values
     */
    protected float instrumentGridHeight = 0;
    /**
     * Indicates if the Y positioning is measured from the bottom.
     */
    protected boolean measureFromBottom = true;
    /**
     * If measured from the top, the class needs the screen height.
     */
    protected int screenHeight = 0;
    /**
     * Tracks, how quick the recalculations must be done
     */
    protected float minimalCalculationLoopTime = 0f;
    
    /**
     * Measured time since the last instrument update calculations
     */
    protected float timeSinceLastUpdate = 0f;

    /**
     * Size of the instrument (width and height)
     */
    protected float instrumentWidth = 0;
    /**
     * Size of the instrument (width and height)
     */
    protected float instrumentHeight = 0;
    /**
     * Half size of the instrument (for reducing calculations)
     */
    protected float instrumentHalfWidth = 0f;
    /**
     * Half size of the instrument (for reducing calculations)
     */
    protected float instrumentHalfHeight = 0f;
    /**
     * Position of the instrument x coordinate
     */
    protected float instrumentCenterPosX = 0f;
    /**
     * Position of the instrument y coordinate
     */
    protected float instrumentCenterPosY = 0f;
    /**
     * Position of the instrument left side
     */
    protected float instrumentLeftPosX = 0f;
    /**
     * Position of the instrument lower side
     */
    protected float instrumentLowerPosY = 0f;

    /**
     * Constructor
     *
     * @param guiNode Reference to the guiNode
     * @param assetManager For loading the images
     * @param instrumentLeftGridPosX position of the left border (in grid size
     * measures)
     * @param instrumentLowerGridPos position of the lower border (in grid size
     * measures)Y
     * @param hudReferenceSize Sise of one grid measure
     * @param instrumentGridWidth Width of the instrument in grid measures
     * @param instrumentGridHeight Height of the instrument in grid measures
     * @param screenHeight Height of the screen in pixel
     * @param measureFromBottom Set to true, if the LowerGridPos should be
     * measured from the bottom. Set it to fals in order to measure it from the
     * top
     */
    public InstrumentControl(
            Node guiNode,
            AssetManager assetManager,
            float instrumentLeftGridPosX,
            float instrumentLowerGridPosY,
            float hudReferenceSize,
            float instrumentGridWidth,
            float instrumentGridHeight,
            int screenHeight,
            boolean measureFromBottom,
            float minimalCalculationLoopTime) {
        // take over the references
        this.guiNode = guiNode;
        this.assetManager = assetManager;
        this.instrumentLeftGridPosX = instrumentLeftGridPosX;
        this.instrumentLowerGridPosY = instrumentLowerGridPosY;
        this.hudReferenceSize = hudReferenceSize;
        this.instrumentGridWidth = instrumentGridWidth;
        this.instrumentGridHeight = instrumentGridHeight;
        this.screenHeight = screenHeight;
        this.measureFromBottom = measureFromBottom;
        this.minimalCalculationLoopTime = minimalCalculationLoopTime;

        // calculate the width and height values
        this.instrumentWidth = this.instrumentGridWidth * this.hudReferenceSize;
        this.instrumentHeight = this.instrumentGridHeight * this.hudReferenceSize;
        this.instrumentHalfWidth = this.instrumentWidth / 2;
        this.instrumentHalfHeight = this.instrumentHeight / 2;

        // calculate position information
        this.instrumentLeftPosX = this.instrumentLeftGridPosX * this.hudReferenceSize;
        if (this.measureFromBottom) {
            this.instrumentLowerPosY = this.instrumentLowerGridPosY * this.hudReferenceSize;
        } else {
            this.instrumentLowerPosY = this.screenHeight
                    - (this.instrumentLowerGridPosY * this.hudReferenceSize);
        }

        this.instrumentCenterPosX = this.instrumentLeftPosX + this.instrumentHalfWidth;
        this.instrumentCenterPosY = this.instrumentLowerPosY + this.instrumentHalfHeight;
        this.centralInstrumentNode = new Node();
        this.guiNode.attachChild(this.centralInstrumentNode);
    }

    /**
     * Creates an picture element and places it to the center of the instrument
     * with relative coordinates
     *
     * @param imageName Name of the image to load for further references
     * @param fileName Filename to load
     * @param positionX Position on the screen (X coord)
     * @param positionY Position on the screen (Y coord)
     * @return Generated picture element
     */
    protected Picture createInstrumentElement(String imageName, String fileName,
            float width, float height, float positionX, float positionY) {
        Picture newElement = new Picture(imageName);
        newElement.setImage(assetManager, fileName, true);
        newElement.setWidth(width);
        newElement.setHeight(height);
        newElement.setLocalTranslation(positionX, positionY, 0);
        return newElement;
    }

    /**
     * Creates an picture element and places lower left aligned to the
     * instrument. The size is inherited from the instrument size.
     *
     * @param imageName Name of the image to load
     * @param fileName Filename to load
     * @return Generated picture element
     */
    protected Picture createInstrumentElementAbsolute(String imageName, String fileName) {
        return this.createInstrumentElement(imageName, fileName,
                this.instrumentWidth, this.instrumentHeight,
                this.instrumentLeftPosX, this.instrumentLowerPosY);
    }

    /**
     * Creates an picture element and places it to the instrument. It will be
     * shifted half size left and down in order to allow a rotation around the
     * center. The idea is that you place a node towards the center of the
     * instrument - which will be the rotation point. Then you attach this
     * picture to this rotation point - but must shift it left and down in order
     * to align it again with the instrument. The size is inherited from the
     * instrument size. with relative coordinates
     *
     * @param imageName Name of the image to load
     * @param fileName Filename to load
     * @return Generated picture element
     */
    protected Picture createInstrumentElementRelativeCenter(String imageName, String fileName) {
        return this.createInstrumentElement(imageName, fileName,
                this.instrumentWidth, this.instrumentHeight,
                -this.instrumentHalfWidth, -this.instrumentHalfHeight);
    }

    /**
     * Shows or hides the element depending on the parameter
     *
     * @param doShow If true, the element will be shown and acitvated, with true
     * it will be hidden and deactivated
     */
    public void showElements(boolean doShow) {
        if (doShow) {
            this.showElements();
        } else {
            this.hideElements();
        }
    }

    /**
     * Hide the element and deactivate it
     */
    public void hideElements() {
        if (this.isEnabled()) {
            this.centralInstrumentNode.setCullHint(Spatial.CullHint.Always);
            this.setEnabled(false);
        }
    }

    /**
     * Show the element and activate it
     */
    public void showElements() {
        if (!this.isEnabled()) {
            this.centralInstrumentNode.setCullHint(Spatial.CullHint.Inherit);
            this.setEnabled(true);
        }
    }
}
